package com.mxpio.erp.inventory.policy.impl;

import com.mxpio.erp.common.LotStatusUtil;
import com.mxpio.erp.common.enums.OrderEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.inventory.entity.*;
import com.mxpio.erp.common.jpa.model.LotKey;
import com.mxpio.erp.common.jpa.model.Order;
import com.mxpio.erp.common.order.policy.OrderContext;
import com.mxpio.erp.common.order.service.OrderLineService;
import com.mxpio.erp.common.quality.entity.*;
import com.mxpio.erp.common.technology.entity.Item;
import com.mxpio.erp.common.technology.entity.ItemStoreProp;
import com.mxpio.erp.common.vo.NoticeClassVo;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpio.erp.inventory.policy.AbstractWoPolicy;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.jpa.query.Operator;
import com.mxpioframework.security.util.SecurityUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

public class IIAInWoPolicy extends AbstractWoPolicy {

	private List<OrderExcuteLineVo> orderExcuteVos;
	private OrderLineService orderLineService;

	public IIAInWoPolicy(List<OrderExcuteLineVo> orderExcuteVos,OrderLineService orderLineService) {
		this.orderExcuteVos = orderExcuteVos;
		this.orderLineService = orderLineService;
	}
	
	@Override
	public void execute(OrderContext context) {
		Order order = context.getEntity();
		if(order instanceof WarehouseOrder) {
			List<OrderExcuteLineVo> orderExcuteVos = context.getOrderExcuteVos();
			List<InvFlow> invFlows = new ArrayList<>();
			
			Map<String, BigDecimal> sumQuantity = new HashMap<String, BigDecimal>();
			List<OrderExcuteLineVo> orderExcuteVosNoLotNo = new ArrayList<>();//无批次的出库明细
			String whCode = ((WarehouseOrder) order).getWhCode();//仓库编码
			String accessType = ((WarehouseOrder) order).getAccessType();
			for(OrderExcuteLineVo vo : orderExcuteVos) {
				InvFlow invFlow = new InvFlow();
				invFlow.setAccessType(accessType);
				invFlow.setItemCode(vo.getItemCode());
				invFlow.setWhCode(((WarehouseOrder) order).getWhCode());
				// 批次控制
				if(OrderEnums.AccessType.IN.getCode().equals(accessType)) {
					if(vo.getLotNo() == null) {
						Lot newLot = context.getLotService().getNewLot(order, vo);
						invFlow.setLotNo(newLot.getLotNo());
						invFlow.setSubLotIndex(newLot.getSubLotIndex());
					}else {
						invFlow.setLotNo(vo.getLotNo());
						String subLotIndex = StringUtils.isBlank(vo.getSubLotIndex())?"0":vo.getSubLotIndex();
						invFlow.setSubLotIndex(subLotIndex);
						LotKey lotKey = new LotKey();
						lotKey.setLotNo(vo.getLotNo());
						lotKey.setSubLotIndex(subLotIndex);
						Lot dbLot = JpaUtil.getOne(Lot.class, lotKey);
						if (dbLot==null){//保存批次
							Lot lot = new Lot();
							lot.setLotNo(vo.getLotNo());
							lot.setLotStatus(LotStatusUtil.toBinaryString("0"));
							lot.setSubLotIndex(subLotIndex);
							JpaUtil.save(lot);
						}
					}
				}else {
					if(vo.getLotNo() == null) {
						/*context.addError("未分配批次");
						break;*/
						orderExcuteVosNoLotNo.add(vo);
						continue;//跳过
					}else {
						invFlow.setLotNo(vo.getLotNo());
						invFlow.setSubLotIndex(vo.getSubLotIndex());
					}
				}
				invFlow.setQuantity(vo.getQuantity());
				// 库存数量控制
				if(OrderEnums.AccessType.OUT.getCode().equals(accessType)) {
					BigDecimal stock = context.getLotService().getStock(invFlow.getWhCode(), invFlow.getItemCode(), invFlow.getLotNo(),invFlow.getSubLotIndex(),false);//此处不再校验hold状态
					if(stock.compareTo(invFlow.getQuantity()) < 0) {
						context.addError("物料：" + invFlow.getItemCode() + "库存不足");
						break;
					}
				}
				
				// 单据数量控制
				BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(vo.getBizWoNo(), vo.getBizWoLineNo());
				BigDecimal currentQuan = sumQuantity.get(vo.getBizWoNo() + "#" + vo.getBizWoLineNo());
				if(currentQuan == null) {
					BigDecimal maxQuantity = vo.getLineQuantity()==null?BigDecimal.ZERO:vo.getLineQuantity();
					currentQuan = maxQuantity.subtract(completeQuantity).subtract(vo.getQuantity());
					sumQuantity.put(vo.getBizWoNo() + "#" + vo.getBizWoLineNo(),currentQuan);//处理拆批情况
				}else {
					currentQuan = currentQuan.subtract(vo.getQuantity());
				}
				if(currentQuan.compareTo(BigDecimal.ZERO) < 0) {
					context.addError("物料：" + invFlow.getItemCode() + "超过最大执行数量");
					break;
				}
				
				invFlow.setMemo(((WarehouseOrder) order).getMemo());
				invFlow.setOriginBizNo(((WarehouseOrder) order).getOriginBizNo());
				invFlow.setOriginBizType(((WarehouseOrder) order).getOriginBizType());
				invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
				invFlow.setWoBizNo(((WarehouseOrder) order).getBizNo());
				invFlow.setWoBizType(((WarehouseOrder) order).getWoType());
				invFlow.setWoBizLineNo(vo.getBizWoLineNo());
				invFlows.add(invFlow);
			}
			//处理出库无批次明细数据
			if (orderExcuteVosNoLotNo.size()>0){
				Map<String, BigDecimal> sumUseItemQuantityMap = new HashMap<String, BigDecimal>();//汇总明细行中相同物料情况，

				Map<String,List<VInvLotStock>> stockListMap = new HashMap<>();
				List<InvFlow> insertFlowList = new ArrayList<>();
				Map<String,BigDecimal> usedStockMap = new HashMap<>();//批次已占用库存
				for(OrderExcuteLineVo vo : orderExcuteVosNoLotNo) {
					// 库存数量控制
					if(OrderEnums.AccessType.OUT.getCode().equals(accessType)) {
						BigDecimal stock = context.getLotService().getItemLiveStockWithLotStatus(whCode, vo.getItemCode(),true);//查询批次正常库存
						BigDecimal useQuantitySum = sumUseItemQuantityMap.get(vo.getItemCode());//该循环中，该物料对应行已经占用数量和
						if (useQuantitySum==null){
							sumUseItemQuantityMap.put(vo.getItemCode(),vo.getQuantity());
							if(stock.compareTo(vo.getQuantity()) < 0) {
								context.addError("物料：" + vo.getItemCode() + "实时库存不足");
								break;
							}
						}else{
							if(stock.compareTo(vo.getQuantity().add(useQuantitySum)) < 0) {
								context.addError("物料：" + vo.getItemCode() + "实时库存不足");
								break;
							}
							sumUseItemQuantityMap.put(vo.getItemCode(),vo.getQuantity().add(useQuantitySum));//累计数量
						}
					}
					// 单据数量控制
					BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(vo.getBizWoNo(), vo.getBizWoLineNo());
					BigDecimal currentQuan = sumQuantity.get(vo.getBizWoNo() + "#" + vo.getBizWoLineNo());//剩余最大可执行数量
					if(currentQuan == null) {
						BigDecimal maxQuantity = vo.getLineQuantity()==null?BigDecimal.ZERO:vo.getLineQuantity();
						currentQuan = maxQuantity.subtract(completeQuantity).subtract(vo.getQuantity());
						sumQuantity.put(vo.getBizWoNo() + "#" + vo.getBizWoLineNo(),currentQuan);//处理拆批情况
					}else {
						currentQuan = currentQuan.subtract(vo.getQuantity());
					}
					if(currentQuan.compareTo(BigDecimal.ZERO) < 0) {
						context.addError("物料：" + vo.getItemCode() + "超过最大执行数量");
						break;
					}

					//开始匹配库存
					List<VInvLotStock> itemLotStockList = null;
					if (stockListMap.containsKey(vo.getItemCode())){
						itemLotStockList = stockListMap.get(vo.getItemCode());
					}else{
						//根据先进先出，查询批次
						Criteria lotStockCriteria = Criteria.create().addCriterion("whCode", Operator.EQ, whCode).
								addCriterion("itemCode",Operator.EQ, vo.getItemCode()).
								addCriterion("lotStatus",Operator.EQ, LotStatusUtil.toBinaryString("0"));//增加批次状态过滤
						lotStockCriteria.addOrder(new com.mxpioframework.jpa.query.Order("createTime",false));//根据创建时间，先进(批次号小)先出。
						itemLotStockList = JpaUtil.linq(VInvLotStock.class).gt("quantity",0).where(lotStockCriteria).list();//过滤调批次为零的
						stockListMap.put(vo.getItemCode(),itemLotStockList);
					}
					BigDecimal quantity = vo.getQuantity();//vo行待执行数据
					for (VInvLotStock stock:itemLotStockList){
						BigDecimal stockQuantity = stock.getQuantity();//该批次库存数量
						if (usedStockMap.containsKey(stock.getLotNo())){
							BigDecimal used = usedStockMap.get(stock.getLotNo());
							stockQuantity = stockQuantity.subtract(used);
						}
						if (stockQuantity.compareTo(BigDecimal.ZERO)<=0){//库存小于等于0，直接跳过
							continue;
						}
						if (quantity.compareTo(stockQuantity)>0){//大于该批次库存数量
							InvFlow invFlow = new InvFlow();
							invFlow.setAccessType(accessType);
							invFlow.setItemCode(vo.getItemCode());
							invFlow.setWhCode(whCode);
							invFlow.setQuantity(stockQuantity);//取库存批次数量
							invFlow.setMemo(((WarehouseOrder) order).getMemo());
							invFlow.setOriginBizNo(((WarehouseOrder) order).getOriginBizNo());
							invFlow.setOriginBizType(((WarehouseOrder) order).getOriginBizType());
							invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
							invFlow.setWoBizNo(((WarehouseOrder) order).getBizNo());
							invFlow.setWoBizType(((WarehouseOrder) order).getWoType());
							invFlow.setWoBizLineNo(vo.getBizWoLineNo());
							invFlow.setLotNo(stock.getLotNo());
							invFlow.setSubLotIndex(stock.getSubLotIndex());//增加子批索引
							quantity = quantity.subtract(stockQuantity);//
							insertFlowList.add(invFlow);
							if (usedStockMap.containsKey(invFlow.getLotNo())){
								BigDecimal used = usedStockMap.get(invFlow.getLotNo());
								usedStockMap.put(invFlow.getLotNo(),used.add(invFlow.getQuantity()));
							}else{
								usedStockMap.put(invFlow.getLotNo(),invFlow.getQuantity());
							}
						}else{//小于等于批次库存的情况
							InvFlow invFlow = new InvFlow();
							invFlow.setAccessType(accessType);
							invFlow.setItemCode(vo.getItemCode());
							invFlow.setWhCode(whCode);
							invFlow.setQuantity(quantity);
							invFlow.setMemo(((WarehouseOrder) order).getMemo());
							invFlow.setOriginBizNo(((WarehouseOrder) order).getOriginBizNo());
							invFlow.setOriginBizType(((WarehouseOrder) order).getOriginBizType());
							invFlow.setOriginBizLineNo(vo.getBizOrderlineNo());
							invFlow.setWoBizNo(((WarehouseOrder) order).getBizNo());
							invFlow.setWoBizType(((WarehouseOrder) order).getWoType());
							invFlow.setWoBizLineNo(vo.getBizWoLineNo());
							invFlow.setLotNo(stock.getLotNo());
							invFlow.setSubLotIndex(stock.getSubLotIndex());//增加子批索引
							//JpaUtil.save(invFlow);
							insertFlowList.add(invFlow);
							if (usedStockMap.containsKey(invFlow.getLotNo())){
								BigDecimal used = usedStockMap.get(invFlow.getLotNo());
								usedStockMap.put(invFlow.getLotNo(),used.add(invFlow.getQuantity()));
							}else{
								usedStockMap.put(invFlow.getLotNo(),invFlow.getQuantity());
							}
							break;//跳出
						}
					}
				}
				if (insertFlowList.size()>0){
					invFlows.addAll(insertFlowList);//添加到待存列表中
				}
			}

			if(CollectionUtils.isEmpty(context.getErrors())) {
				JpaUtil.save(invFlows);
			}
		}else {
			context.addError("单据类型错误");
		}
	}
	
	@Override
	public OrderContext config(OrderContext context) {
		context.setOrderExcuteVos(orderExcuteVos);
		return context;
	}

	@Override
	public boolean beforeExecute(OrderContext context) {
		return true;
	}

	@Override
	public void afterExecute(OrderContext context) {
		if(CollectionUtils.isEmpty(context.getErrors())){
			Order wo = context.getEntity();
			if (judgeWOCompletion(context)){
				if(StringUtils.isBlank(wo.getExecutor())) {
					wo.setExecuteTime(new Date());
					wo.setExecutor(SecurityUtils.getLoginUsername());
				}
				wo.setOrderStatus(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode());
				JpaUtil.update(wo);
			}else{
				if(OrderStatusEnums.OrderStatus.ORDERED.getStatusCode().equals(wo.getOrderStatus())||
						OrderStatusEnums.OrderStatus.PLAN.getStatusCode().equals(wo.getOrderStatus())) {
					wo.setOrderStatus(OrderStatusEnums.OrderStatus.PROCESSING.getStatusCode());
					wo.setExecuteTime(new Date());
					wo.setExecutor(SecurityUtils.getLoginUsername());
					JpaUtil.update(wo);
				}
			}

			WarehouseOrder warehouseOrder = (WarehouseOrder) wo;
			String originBizNo = warehouseOrder.getOriginBizNo();//源单编码
			if(StringUtils.isNotBlank(originBizNo)){
				InventoryInspectApply inspectApply = JpaUtil.getOne(InventoryInspectApply.class, originBizNo);
				if (inspectApply!=null){
					boolean orderCompletionFlag = true;//库存检验申请是否执行完成
					List<InventoryInspectApplyLine> scrapOutApplyLines = JpaUtil.linq(InventoryInspectApplyLine.class).equal("bizNo", inspectApply.getBizNo()).list();
					if (scrapOutApplyLines.size()>0){
						NoticeClassVo scrapOutNoticeClazz = new NoticeClassVo(InventoryQualityOrder.class, InventoryQualityOrderLine.class);
						List<NoticeClassVo> noticeClassVos = new ArrayList<NoticeClassVo>();
						noticeClassVos.add(scrapOutNoticeClazz);
						orderLineService.handleBizOrderLineQuantity(scrapOutApplyLines, noticeClassVos, OrderEnums.AccessType.IN.getCode());

						for (InventoryInspectApplyLine salesOrderLine:scrapOutApplyLines){
							BigDecimal quantity = salesOrderLine.getQuantity();//订单行数量
							BigDecimal actualQuantity = salesOrderLine.getActualQuantity();//已执行数量
							//BigDecimal actualRejectQuantity = salesOrderLine.getActualRejectQuantity();//此部分为自动出数量
							BigDecimal planQuantity = salesOrderLine.getPlanQuantity();//计划执行数量
							BigDecimal planRejectQuantity = salesOrderLine.getPlanRejectQuantity();//由于是自动出，故此 计划退货数量为零
							//判断订单行是否执行完成：1计划数量等于零，2已执行数量减去已执行退货数量等于订单行数量
							if (planQuantity.compareTo(BigDecimal.ZERO)!=0
									//||planRejectQuantity.compareTo(BigDecimal.ZERO)!=0
									||quantity.compareTo(actualQuantity)!=0){
								orderCompletionFlag = false;
								break;
							}
						}
						if (orderCompletionFlag){
							inspectApply.setOrderStatus(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode());//完成
							inspectApply.setExecuteTime(new Date());
							JpaUtil.update(inspectApply);
						}
					}
				}
			}
		}
	}

	private boolean judgeWOCompletion(OrderContext context){//判断仓单是否已执行完毕
		Order order = context.getEntity();
		List<WarehouseOrderLine> lines = JpaUtil.linq(WarehouseOrderLine.class).equal("bizNo", order.getBizNo()).list();
		if (lines.size()>0){
			String accessType = ((WarehouseOrder) order).getAccessType();
			if(OrderEnums.AccessType.OUT.getCode().equals(accessType)) {
				Set<String> itemCodeSet = lines.stream().map(WarehouseOrderLine::getItemCode).collect(Collectors.toSet());
				List<ItemStoreProp> itemStorePropList = JpaUtil.linq(ItemStoreProp.class).equal("propType", "item").in("code", itemCodeSet).list();
				if (itemStorePropList.size()>0){
					List<Item> itemList = JpaUtil.linq(Item.class).in("itemCode", itemCodeSet).list();
					Map<String, Item> itemMap = JpaUtil.index(itemList, "itemCode");
					Map<String, ItemStoreProp> itemStorePropMap = JpaUtil.index(itemStorePropList, "code");
					for (WarehouseOrderLine line:lines){
						String itemCode = line.getItemCode();
						if (itemStorePropMap.containsKey(itemCode)){
							ItemStoreProp itemStoreProp = itemStorePropMap.get(itemCode);
							Item item = itemMap.get(itemCode);
							if (itemStoreProp!=null&&itemStoreProp.getToleranceRange()!=null&&itemStoreProp.getToleranceRange().compareTo(BigDecimal.ZERO)>0){
								BigDecimal toleranceRange = itemStoreProp.getToleranceRange().divide(new BigDecimal("100"));//转化成小数
								Integer scale = item.getScale();//精度
								BigDecimal lineQuantity = line.getQuantity();
								BigDecimal toleranceRangeQuantity = lineQuantity.multiply(toleranceRange);
								line.setToleranceRangeQuantity(toleranceRangeQuantity.setScale(scale, RoundingMode.CEILING));
							}
						}
						if (line.getToleranceRangeQuantity()==null){//为空就指定为0
							line.setToleranceRangeQuantity(BigDecimal.ZERO);
						}
					}
				}
				for (WarehouseOrderLine line:lines){
					BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(line.getBizNo(), line.getLineNo());
					if (line.getQuantity().subtract(line.getToleranceRangeQuantity()).compareTo(completeQuantity)>0){
						return false;
					}
				}
			}else{//入库操作还按之前的校验逻辑
				for (WarehouseOrderLine line:lines){
					BigDecimal completeQuantity = context.getLotService().getMaxQuantityByWo(line.getBizNo(), line.getLineNo());
					if (line.getQuantity().compareTo(completeQuantity)!=0){
						return false;
					}
				}
			}
		}else{
			return false;
		}
		return true;
	}
}
